<?php

namespace App\Utility;

use App\Utility\Pool\RedisPool;
use EasySwoole\Component\Pool\PoolManager;
use EasySwoole\EasySwoole\Config;

class Cache{

    protected $redis;
    protected $options;
    protected $queue;
    protected $queuePrefix = 'queue:';

    public function __construct(){
        $options = Config::getInstance()->getConf('redis');
        $redis = PoolManager::getInstance()->getPool(RedisPool::class)
            ->getObj($options['pool_time_out']);
        if ($redis) {
            $this->redis = $redis;
            $this->options = $options;
            $this->queue = $this->queuePrefix.$options['queue'];
        } else {
            //直接抛给异常处理，不往下
            throw new \Exception('error,Redis Pool is Empty');
        }
    }

    public function __destruct(){
        PoolManager::getInstance()->getPool(RedisPool::class)->recycleObj($this->redis);
    }

    public function onQueue($queue)
    {
        $this->queue = $this->queuePrefix.$queue;
        return $this;
    }

    //事务开始
    public function multi(){
        return $this->redis->multi();
    }

    //事务完成
    public function exec(){
        return $this->redis->exec();
    }

    //入列
    function push($data){
        return $this->redis->lpush($this->queue, json_encode($data));
    }
    //出列
    function read(){
        return json_decode($this->redis->rPop($this->queue),true);
    }

    //获取指定 key 的值。
    public function get($name, $default = null){
        $key   = $this->getCacheKey($name);
        $value = $this->redis->get($key);
        if (is_null($value) || false === $value) {
            return $default;
        }
        return is_numeric($value) ? $value : $this->unPackData($value);
    }

    //设置指定 key 的值
    public function set($name, $value, $expire = null){
        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }
        $key   = $this->getCacheKey($name);
        $expire= $this->getExpireTime($expire);
        $value = is_numeric($value) ? $value : $this->packData($value);
        if ($expire) {
            $result = $this->redis->setex($key, $expire, $value);
        } else {
            $result = $this->redis->set($key, $value);
        }
        return $result;
    }


    //将 key 所储存的值加上给定的增量值
    public function inc($name, $step = 1){
        return $this->redis->incrBy($this->getCacheKey($name), $step);
    }

    //key 所储存的值减去给定的减量值
    public function dec($name, $step = 1){
        return $this->redis->decrBy($this->getCacheKey($name), $step);
    }

    //获取指定 key 的值并删除key。
    public function pull($name){
        $result = $this->get($name);
        if ($result) {
            $this->delete($name);
            return $result;
        }
        return null;
    }

    //设置key一个值,不存在则设置该值
    public function remember($name, $value, $expire = null){
        if (!$this->has($name)) {
            return $this->set($name, $value, $expire);
        }
        return $this->get($name);
    }


    //删除指定key
    public function delete($name){
        return $this->redis->delete($this->getCacheKey($name));
    }

    //检查给定 key 是否存在
    public function has($name){
        return $this->redis->exists($this->getCacheKey($name));
    }

    //清除所有缓存
    public function clear(){
        $keys = $this->redis->keys($this->options['prefix'].'*');
        if ($keys) {
            $this->redis->del(...$keys);
        }
        return true;
    }

    //获取缓存前缀
    protected function getCacheKey($name){
        return $this->options['prefix'].$name;
    }

    //过期时间
    protected function getExpireTime($expire){
        if ($expire instanceof \DateTime) {
            $expire = $expire->getTimestamp() - time();
        }
        return (int) $expire;
    }

    //序列化数据
    protected function packData($data){
        return serialize($data);
    }

    //反序列化数据
    protected function unPackData($data){
        return unserialize($data);
    }

    //向集合中增加元素
    public function zAdd($key, $score, $value){
        return $this->redis->zAdd($key, $score, $value);
    }
    //统计集合成员数
    public function zCard($key){
        return $this->redis->zCard($key);
    }
    //返回有序集中，指定区间内的成员。
    public function zRange($key, $start, $end, $scores=null){
        return $this->redis->zRange($key, $start, $end, $scores);
    }

    //移除有序集中，指定分数（score）区间内的所有成员。
    public function zRemRangeByScore($key, $min, $max){
        return $this->redis->zRemRangeByScore($key, $min, $max);
    }

    //移除集合中一个成员
    public function zrem($key,$value){
        return $this->redis->zrem($key,$value);
    }

    //判断 member 元素是否是集合 key 的成员
    public function sismember($key, $value){
        return $this->redis->sismember($key, $value);
    }

    //返回集合中的所有成员
    public function sMembers($key){
        return $this->redis->sMembers($key);
    }

    //移除并返回集合中的一个随机元素
    public function sPop($key){
        return $this->redis->sPop($key);
    }

    //向集合中增加元素
    public function sAdd($key, $value){
        return $this->redis->sAdd($key, $value);
    }

    //所有给定集合的并集存储在 destination 集合中
    public function sUnionStore($dst, $key, $other_keys=null){
        return $this->redis->sUnionStore($dst, $key, $other_keys);
    }

    //获取集合的成员数
    public function scard($key){
        return $this->redis->scard($key);
    }

    //移除集合中一个成员
    public function srem($key,$value){
        return $this->redis->srem($key,$value);
    }

    //返回集合中的一个或多个随机元素
    public function sRandMember($key, $count=null){
        return $this->redis->sRandMember($key,$count);
    }

    //返回有序集合中指定分数区间的成员列表。有序集成员按分数值递增(从小到大)次序排列。
    public function zRangeByScore($key, $start, $end, $options=null){
        return $this->redis->zRangeByScore($key, $start, $end, $options);
    }

    //设置 key 的过期时间，key 过期后将不再可用。单位以秒计。
    public function expire($key, $integer){
        return $this->redis->expire($key, $integer);
    }

    //查询 key 的过期时间。
    public function ttl($key){
        return $this->redis->ttl($key);
    }



    //向无序集合中批量增加元素
    public function sAddBatch($key,$value){
        if(is_array($value)){
            array_walk($value,function (&$item){
                $item = $this->packData($item);
            });
            array_unshift($value,$key);
            return call_user_func_array(array($this->redis, "sAdd"),$value);
        }else{
            return $this->redis->sAdd($key, $this->packData($value));
        }
    }


    //返回无序集合中的一个或多个随机元素
    public function sRandBatch($key, $count=null){
        $data = $this->redis->sRandMember($key,$count);
        if(is_array($data)){
            array_walk($data,function (&$item){
                $item = $this->unPackData($item);
            });
        }else{
            $data = $this->unPackData($data);
        }
        return $data;
    }







}
